package com.ruoyi.pay.refund.wx.util;

import com.alibaba.fastjson.JSON;
import com.aliyun.oss.ServiceException;
import com.ruoyi.config.mapper.ApplicationConfigMapper;
import com.ruoyi.config.mapper.WxpayConfigMapper;
import com.wechat.pay.java.core.Config;
import com.wechat.pay.java.core.RSAAutoCertificateConfig;
import com.wechat.pay.java.core.exception.HttpException;
import com.wechat.pay.java.core.exception.MalformedMessageException;
import com.wechat.pay.java.service.transferbatch.TransferBatchService;
import com.wechat.pay.java.service.transferbatch.model.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.io.IOException;
import java.math.BigDecimal;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.ArrayList;
import java.util.Base64;
import java.util.Date;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;

@Slf4j
@Component
public class WxTransferUtil {
    @Resource
    private WxpayConfigMapper wxpayConfigMapper;
    @Resource
    private ApplicationConfigMapper applicationConfigMapper;


    /**
     * 商家向微信用户转账 (提现到微信零钱)
     *
     * @param openId         【收款用户openid】 商户appid下，某用户的openid
     * @param OutBatchNo     【商家批次单号】 商户系统内部的商家批次单号，要求此参数只能由数字、大小写字母组成，在商户系统内部唯一
     * @param outDetailNo    【商家明细单号】 商户系统内部区分转账批次单下不同转账明细单的唯一标识，要求此参数只能由数字、大小写字母组成
     * @param transferAmount 【转账金额】 转账金额单位为“分”
     * @param transferRemark 【转账备注】 单条转账备注（微信用户会收到该备注），UTF8编码，最多允许32个字符
     */
    public String WxTransfer(String OutBatchNo, String openId, String outDetailNo, BigDecimal transferAmount, String transferRemark) throws IOException {

        try {
            String mchId = wxpayConfigMapper.selectOneData("mchId");
            String mchSerialNo = wxpayConfigMapper.selectOneData("mchSerialNo");
            String privateKeyPath = wxpayConfigMapper.selectOneData("privateKeyPath");
            String apiV3Key = wxpayConfigMapper.selectOneData("apiV3Key");
            String appAppid = applicationConfigMapper.selectData("app_appid");
            Config config = new RSAAutoCertificateConfig.Builder() .merchantId(mchId)
                    .privateKey(getPrivateKey(privateKeyPath))
                    .merchantSerialNumber(mchSerialNo)
                    .apiV3Key(apiV3Key)
                    .build();
            TransferBatchService service = new TransferBatchService.Builder().config(config).build();
            InitiateBatchTransferRequest initiateBatchTransferRequest =
                    new InitiateBatchTransferRequest();
            initiateBatchTransferRequest.setAppid(appAppid);
            //generateOrderSn()
            initiateBatchTransferRequest.setOutBatchNo(OutBatchNo);
            initiateBatchTransferRequest.setBatchName("提现到零钱");
            initiateBatchTransferRequest.setBatchRemark("提现到零钱");
            initiateBatchTransferRequest.setTotalAmount(transferAmount.longValue());
            initiateBatchTransferRequest.setTotalNum(1);
            {
                List<TransferDetailInput> transferDetailListList = new ArrayList<>();
                {
                    TransferDetailInput transferDetailInput = new TransferDetailInput();
                    //【商家明细单号】 商户系统内部区分转账批次单下不同转账明细单的唯一标识，要求此参数只能由数字、大小写字母组成
                    transferDetailInput.setOutDetailNo(outDetailNo);
                    //【转账金额】 转账金额单位为“分”
                    transferDetailInput.setTransferAmount(transferAmount.longValue());
                    //【转账备注】 单条转账备注（微信用户会收到该备注），UTF8编码，最多允许32个字符
                    transferDetailInput.setTransferRemark(transferRemark);
                    //【收款用户openid】 商户appid下，某用户的openid
                    transferDetailInput.setOpenid(openId);
                    transferDetailListList.add(transferDetailInput);
                }
                initiateBatchTransferRequest.setTransferDetailList(
                        transferDetailListList);
            }

            log.info("微信提现入参：{}", JSON.toJSONString(initiateBatchTransferRequest, true));
            InitiateBatchTransferResponse response =
                    service.initiateBatchTransfer(initiateBatchTransferRequest);
            log.info("提现返回信息：{}", JSON.toJSONString(response));
            if (response.getBatchStatus().equals("ACCEPTED")) {
                log.info("转账申请已受理");
            }
            return response.getBatchStatus();
        } catch (HttpException e) { // 发送HTTP请求失败
            // 调用e.getHttpRequest()获取请求打印日志或上报监控，更多方法见HttpException定义
            log.error(e.getMessage());
            throw new RuntimeException("微信提现失败:"+e.getMessage());
        } catch (ServiceException e) { // 服务返回状态小于200或大于等于300，例如500
            // 调用e.getResponseBody()获取返回体打印日志或上报监控，更多方法见ServiceException定义
            log.error(e.getMessage());
            throw new RuntimeException("微信提现失败:"+e.getMessage());
        } catch (MalformedMessageException e) { // 服务返回成功，返回体类型不合法，或者解析返回体失败
            // 调用e.getMessage()获取信息打印日志或上报监控，更多方法见MalformedMessageException定义
            log.error(e.getMessage());
            throw new RuntimeException("微信提现失败:"+e.getMessage());
        } catch (Exception e) {
            log.error(e.getMessage());
            e.printStackTrace();
            throw new RuntimeException("微信提现失败:"+e.getMessage());
        }

    }

    public PrivateKey getPrivateKey(String url) {
        log.info("weChatPayPrivateKeyUrl:{}", url);

        try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
            HttpGet request = new HttpGet(url);
            try (CloseableHttpResponse response = httpClient.execute(request)) {
                String content = EntityUtils.toString(response.getEntity(), "utf-8");
                String privateKey = content.replace("-----BEGIN PRIVATE KEY-----", "")
                        .replace("-----END PRIVATE KEY-----", "")
                        .replaceAll("\\s+", "");
                KeyFactory kf = KeyFactory.getInstance("RSA");
                PrivateKey key = kf.generatePrivate(new PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKey)));
                return key;
            }
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("当前Java环境不支持RSA", e);
        } catch (InvalidKeySpecException e) {
            throw new RuntimeException("无效的密钥格式");
        } catch (IOException e) {
            e.printStackTrace();
        }
        log.info("获取私钥已完成");
        return null;
    }
    /**
     * 通过商家批次单号查询批次单
     *
     * @param outBatchNo  商家转账批次单号
     * @param outDetailNo 商家转账明细单号
     */
    public String transferQuery(String outBatchNo, String outDetailNo) throws IOException {

        try {
            String mchId = wxpayConfigMapper.selectOneData("mchId");
            String mchSerialNo = wxpayConfigMapper.selectOneData("mchSerialNo");
            String privateKeyPath = wxpayConfigMapper.selectOneData("privateKeyPath");
            String apiV3Key = wxpayConfigMapper.selectOneData("apiV3Key");
            Config config = new RSAAutoCertificateConfig.Builder() .merchantId(mchId)
                    .privateKeyFromPath(privateKeyPath)
                    .merchantSerialNumber(mchSerialNo)
                    .apiV3Key(apiV3Key)
                    .build();
            TransferBatchService transferBatchService = new TransferBatchService.Builder().config(config).build();
            GetTransferDetailByOutNoRequest request = new GetTransferDetailByOutNoRequest();
            request.setOutBatchNo(outBatchNo);
            request.setOutDetailNo(outDetailNo);
            TransferDetailEntity response = transferBatchService.getTransferDetailByOutNo(request);
            log.info("查询提现返回信息：{}", JSON.toJSONString(response));
            if ("FAIL".equals(response.getDetailStatus())) {
                log.info("提现失败");
            }
            if ("WAIT_PAY".equals(response.getDetailStatus())) {
                log.info("待商户确认, 符合免密条件时, 系统会自动扭转为提现中");
            }
            if ("PROCESSING".equals(response.getDetailStatus())) {
                log.info("提现中。正在处理中，提现结果尚未明确");
            }
            if ("SUCCESS".equals(response.getDetailStatus())) {
                log.info("提现成功");
            }
            return response.getDetailStatus();
        } catch (HttpException e) { // 发送HTTP请求失败
            // 调用e.getHttpRequest()获取请求打印日志或上报监控，更多方法见HttpException定义
            log.error(e.getMessage());
            throw new RuntimeException("微信提现失败:"+e.getMessage());
        } catch (ServiceException e) { // 服务返回状态小于200或大于等于300，例如500
            // 调用e.getResponseBody()获取返回体打印日志或上报监控，更多方法见ServiceException定义
            log.error(e.getMessage());
            throw new RuntimeException("微信提现失败:"+e.getMessage());
        } catch (MalformedMessageException e) { // 服务返回成功，返回体类型不合法，或者解析返回体失败
            // 调用e.getMessage()获取信息打印日志或上报监控，更多方法见MalformedMessageException定义
            log.error(e.getMessage());
            throw new RuntimeException("微信提现失败:"+e.getMessage());
        } catch (Exception e) {
            log.error(e.getMessage());
            e.printStackTrace();
            throw new RuntimeException("微信提现失败:"+e.getMessage());
        }
    }

    public String generateOrderSn() {
        String time = cn.hutool.core.date.DateUtil.format(new Date(), "yyyyMMddHHmmssSSS").substring(2);
        int randomInt = ThreadLocalRandom.current().nextInt(1000, 10000);
        return time + randomInt;
    }


}
